/*
 * EK.Dispatcher
 * Inspired by Rails Routes
 * 
 * Enrichment Kit @ek.version@ (http://enrichmentkit.com)
 * Copyright (c) 2008 Pink Hominid
 * Licensed under terms of the MIT License (LICENSE.txt)
 * 
 * IE 6.0+, FF 1.5+ Tested
 * 
 * TODO:
 * 
 * Remove dependency on eval
 */

/**
 * Maps URLs to client-side controllers/actions/params
 * @constructor
 */
EK.Dispatcher = function EkDispatcher() {
    var that = this;
    
    /**
     * Set of named dispatch routes. Routes visited in defined order.
     */
    this.routes = { 
        /*test: {
            pattern: /^[^#]*#\/([^\/]*)\/([^\/]*)\/([^\/]*)\/([^\/]*)/,
            controller: "EK.test.$1",
            action: "$2",
            params: "$3, $4"
        }*/
    };
    
	var tier = 1;
	this.tier = {
		get: function() {
			return tier;
		},
		set: function(newTier) {
			tier = newTier;
			that.x(); // dispatch with new tier
		}
	};
	
    this.controllerFactory = {
        get: function (cName) {
            var c = eval(cName);
            if (typeof c == "function") { c = new c(); }
            return c;
        }       
    };
    
    /**
     * Processes the given URL through all the dispatch routes.
     * @param url {String} The URL to dispatch
     */
    this.x = function (url) {
        var exec = [];
        var unload = [];
        url = url || location.href; // no param defaults to current url
        if (typeof url == "string" && this.routes) {
            for (var routeName in this.routes) {
                var route = this.routes[routeName];
                var matches = undefined;
                if (route.pattern &&
				    route.controller &&
					(route.tier || 1) <= that.tier.get() &&
				    route.pattern.test(url)) {

                    exec.push(route);
                } else {
                    unload.unshift(route);
                }
            }
            for (var i = 0; i < unload.length; i++) {
                if (unloadRoute(unload[i]) === false) { return false; }
            }
            for (var j = 0; j < exec.length; j++) {
                if (execRoute(exec[j], url) === false) { return false; }
            }

        }
    };
    
    function execRoute(route, url) {
        var ctrlrStr = route.controller;
        var actionStr = route.action;
        var paramsStr = route.params;
        var matches = route.pattern.exec(url);
        for (var i = 1; i < matches.length; i++) {
            ctrlrStr = ctrlrStr.replace("$" + i, matches[i]);
            if (actionStr) {
                actionStr = actionStr.replace("$" + i,
                     matches[i]); 
            }
            if (paramsStr) {
                paramsStr = paramsStr.replace("$" + i,
                    matches[i]); 
            }
        }
        var command = new EK.DispatchCommand(ctrlrStr, actionStr, paramsStr);
        if (!command.equals(route.lastCommand)) {
            if (!command.controllerEquals(route.lastCommand)) {
                if (unloadRoute(route) === false) { return false; }
            }
            var result = command.x(that.controllerFactory);
            route.lastCommand = command;
            return result;
        }        
    }
    
    function unloadRoute(route) {
        if (route.lastCommand) {
            var result = null;
            route.lastCommand.action = route.unload || "unload";
            route.lastCommand.params = null;
            if (route.lastCommand.x() === false) {
                return false;
            }
            route.lastCommand = null;
            return true;
        }        
    }
};

EK.DispatchCommand = function EkDispatchCommand(ctrlr, action, params) {
    this.controller = ctrlr;
    this.action = action;
    this.params = params;

    this.controllerRef = undefined;

    this.controllerEquals = function (cmd) {
        if (cmd &&
            (this.controller === cmd.controller))
        {
            return true;
        } else {
            return false;
        }
    };

    this.equals = function (cmd) {
        if (cmd &&
            (this.controller === cmd.controller) &&
            (this.action === cmd.action) &&
            (this.params === cmd.params)) 
        {
            return true; 
        } else {
            return false;
        }
    };
    
    this.x = function(cFactory) {
        //get controller
        if (!this.controllerRef && cFactory) {
            this.controllerRef = cFactory.get(this.controller);
        }
        if (this.controllerRef && this.action && this.controllerRef[this.action]) {
            EK.debug("EK Dispatcher: " + this.controller + "['" + this.action + "'](" + this.params +")");
            return this.controllerRef[this.action].apply(this.controllerRef, 
                this.params ? this.params.split(",") : []);
        } else {
            throw ("EK Dispatcher: " + this.controller + "['" + this.action + "'] not found");
        }
    };
};
